﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using LiveGame;

namespace LiveGame.Tests
{
    [TestClass]
    public class CellTest
    {
        [TestMethod]
        public void ConstructCell_WithAliveStatus_CellIsAlive()
        {
            // Setup
            var cell = new Cell( true, 0, 0 );

            // Act
            var status = cell.Status;

            // Assert
            Assert.IsTrue( status );
        }

        [TestMethod]
        public void ConstructCell_WithDeadStatus_CellIsDead()
        {
            // Setup
            var cell = new Cell( false, 0, 0 );

            // Act
            var status = cell.Status;

            // Assert
            Assert.IsFalse( status );
        }

        [TestMethod]
        [ExpectedException( typeof( ArgumentNullException ) )]
        public void AddNeighbour_NullCell_ThrowArgumentNullException()
        {
            // Setup
            var cell = new Cell( true, 0, 0 );

            // Act
            try
            {
                cell.Add( null );
            }
            catch( ArgumentNullException exception )
            {
                // Assert
                Assert.AreEqual( "neighbor", exception.ParamName );
                throw;  // have to throw exception at the end of the test :(
            }
        }

        [TestMethod]
        [ExpectedException( typeof( ArgumentOutOfRangeException ) )]
        public void AddNeighbor_MoreThan9Cells_ThrowArgumentOutOfRangeException()
        {
            // Setup
            var cell = new Cell( true, 0, 0 );

            // Action
            try
            {
                for( int row = 0, column = 0; row < 9; row++ )
                    cell.Add( new Cell( true, row / 3, column < 3 ? column++ : column = 0 ) );
            }
            catch( ArgumentOutOfRangeException exception )
            {
                // Assert
                Assert.AreEqual( "neighbor", ( (ArgumentOutOfRangeException) exception.InnerException ).ParamName );
                throw;
            }
        }

        [TestMethod]
        public void Underpopulation_LessThan2LiveNeighbors_StatusBecomesDie()
        {
            // Setup
            var cell = new Cell( true, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( false, 0, 1 ) );
            cell.Add( new Cell( false, 0, 2 ) );
            cell.Add( new Cell( false, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsFalse( cell.Status );
        }

        [TestMethod]
        public void Overcrowding_MoreThan3LiveNeighbors_StatusBecomesDie()
        {
            // Setup
            var cell = new Cell( true, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( true, 0, 1 ) );
            cell.Add( new Cell( true, 0, 2 ) );
            cell.Add( new Cell( true, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsFalse( cell.Status );
        }

        [TestMethod]
        public void LongLiving_With2LiveNeighbors_StatusStillAlive()
        {
            // Setup
            var cell = new Cell( true, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( true, 0, 1 ) );
            cell.Add( new Cell( false, 0, 2 ) );
            cell.Add( new Cell( false, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsTrue( cell.Status );
        }

        [TestMethod]
        public void LongLiving_With3LiveNeighbors_StatusStillAlive()
        {
            // Setup
            var cell = new Cell( true, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( true, 0, 1 ) );
            cell.Add( new Cell( true, 0, 2 ) );
            cell.Add( new Cell( false, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsTrue( cell.Status );
        }

        [TestMethod]
        public void Revive_LessThan3LiveNeighbors_StatusStillDead()
        {
            // Setup
            var cell = new Cell( false, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( true, 0, 1 ) );
            cell.Add( new Cell( false, 0, 2 ) );
            cell.Add( new Cell( false, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsFalse( cell.Status );
        }

        [TestMethod]
        public void Revive_MoreThan3LiveNeighbors_StatusStillDead()
        {
            // Setup
            var cell = new Cell( false, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( true, 0, 1 ) );
            cell.Add( new Cell( true, 0, 2 ) );
            cell.Add( new Cell( true, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsFalse( cell.Status );
        }

        [TestMethod]
        public void Revive_WithExactly3LiveNeighbors_StatusBecomesAlive()
        {
            // Setup
            var cell = new Cell( false, 1, 1 );

            cell.Add( new Cell( true, 0, 0 ) );
            cell.Add( new Cell( true, 0, 1 ) );
            cell.Add( new Cell( true, 0, 2 ) );
            cell.Add( new Cell( false, 1, 0 ) );
            cell.Add( new Cell( false, 1, 2 ) );
            cell.Add( new Cell( false, 2, 0 ) );
            cell.Add( new Cell( false, 2, 1 ) );
            cell.Add( new Cell( false, 2, 2 ) );

            // Action
            cell.Execute();
            cell.ApplyResult();

            // Assert
            Assert.IsTrue( cell.Status );
        }
    }
}
